@khoohaoyit/image-size
Version:
A stream based image size extractor in Node
160 lines (159 loc) • 9.39 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var g = generator.apply(thisArg, _arguments || []), i, q = [];
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
function fulfill(value) { resume("next", value); }
function reject(value) { resume("throw", value); }
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractHeifSize = exports.extractHeicSize = exports.extractAvifSize = void 0;
const extractSize = (formats, targetFormat) => {
return function (stream) {
return __asyncGenerator(this, arguments, function* () {
const ftypBox = yield __await(readFtypBox(stream, 0));
if (ftypBox.type !== 'ftyp')
return yield __await(void 0);
for (const currentFormat of formats) {
const foundIndex = [ftypBox.minorVersion, ...ftypBox.compatibleBrands].indexOf(currentFormat);
if (foundIndex === -1)
continue;
if (currentFormat !== targetFormat)
return yield __await(void 0);
break;
}
loop: for (let currentBox; currentBox = yield __await(readBox(stream, currentBox
? currentBox.offset + currentBox.size
: ftypBox.offset + ftypBox.size));)
switch (currentBox.type) {
default:
break;
case 'mdat':
return yield __await(void 0);
case 'meta':
{
const parentBox = currentBox;
for (let currentBox; isParentLastChildBox(parentBox, currentBox)
&& (currentBox = yield __await(readBox(stream, currentBox
? currentBox.offset + currentBox.size
: parentBox.itemOffset + 4)));)
switch (currentBox.type) {
default:
break;
case 'iprp':
{
const parentBox = currentBox;
const ipcoBox = yield __await(readBox(stream, parentBox.itemOffset));
if (ipcoBox.type !== 'ipco')
throw new Error(`boxType isn't ipco`);
for (let currentBox; isParentLastChildBox(parentBox, currentBox)
&& (currentBox = yield __await(readBox(stream, currentBox
? currentBox.offset + currentBox.size
: ipcoBox.itemOffset)));)
switch (currentBox.type) {
default:
break;
case 'ispe':
{
const width = yield __await(stream.readUint32BE(currentBox.itemOffset + 4));
const height = yield __await(stream.readUint32BE(currentBox.itemOffset + 4 + 4));
return yield __await(yield yield __await({ width, height }));
}
break;
}
}
break;
// case 'iloc':
// break;
// case 'iinf': {
// const parentBox = currentBox;
// const itemCount = await stream.readUint16BE(parentBox.itemOffset);
// for (
// let currentBox, remain = itemCount
// ; currentBox = await readFullBox(
// stream,
// currentBox
// ? currentBox.offset + currentBox.size
// : parentBox.itemOffset + 2
// ), remain
// ; --remain
// ) switch (currentBox.type) {
// default:
// break;
// case 'infe': {
// const itemId = await stream.readUint16BE(currentBox.itemOffset);
// // const itemName = await stream.readString();
// console.log(itemId);
// } break;
// }
// } break;
}
break loop;
}
break;
}
});
};
};
exports.extractAvifSize = extractSize(['avif', 'heic', 'heif'], 'avif');
exports.extractHeicSize = extractSize(['avif', 'heic', 'heif'], 'heic');
exports.extractHeifSize = extractSize(['avif', 'heic', 'heif'], 'heif');
function readFtypBox(stream, offset) {
return __awaiter(this, void 0, void 0, function* () {
const box = yield readBox(stream, offset);
if (box.type !== 'ftyp')
throw new Error(`Expected ftyp box type, received: ${box.type}`);
const majorBrand = yield stream.readString(box.itemOffset, 4);
const minorVersion = yield stream.readUint32BE(box.itemOffset + 4);
const compatibleBrands = yield stream.readString(box.itemOffset + 8, box.offset + box.size - (box.itemOffset + 8))
.then(text => { var _a; return (_a = text.match(/..../g)) !== null && _a !== void 0 ? _a : []; });
return Object.assign(Object.assign({}, box), { majorBrand,
minorVersion,
compatibleBrands });
});
}
function readBox(stream, offset) {
return __awaiter(this, void 0, void 0, function* () {
const size = yield stream.readUint32BE(offset);
const typeBuffer = yield stream.read(offset + 4, 4);
if ( // http://www.columbia.edu/kermit/ascii.html
typeBuffer[0] < 32 || typeBuffer[0] > 126
|| typeBuffer[1] < 32 || typeBuffer[1] > 126
|| typeBuffer[2] < 32 || typeBuffer[2] > 126
|| typeBuffer[3] < 32 || typeBuffer[3] > 126)
throw new Error(`type isn't a printable characters`);
return {
size: size,
type: typeBuffer.toString(),
offset: offset,
itemOffset: offset + 8,
};
});
}
function readFullBox(stream, offset) {
return __awaiter(this, void 0, void 0, function* () {
const box = yield readBox(stream, offset);
const version = yield stream.readUint8(box.itemOffset);
const flags = yield stream.readUintBE(box.itemOffset + 1, 3);
return Object.assign(Object.assign({}, box), { version, flags, itemOffset: box.itemOffset + 4 });
});
}
function isParentLastChildBox(parentBox, currentBox) {
if (!currentBox)
return true;
return currentBox.offset + currentBox.size < parentBox.offset + parentBox.size;
}